﻿using System;
using FibMatrix.Schedule;
using FM_Mono;
using UnityEngine;

public class ScriptFacade
{
#if (UNITY_WEBGL && !UNITY_EDITOR) || DISABLE_FM_MONO
    private const string CLASS_SUFFIX = ", Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
    private const string ASSEMBLY_NAME = "GameProj";
#endif

    private static ScriptFacade _instance;

    [AcquireOnce]
    public static ScriptFacade instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new ScriptFacade();
            }

            return _instance;
        }
    }

    private int _scriptTickUpdator = -1;

    public void InitializeAndStartup(IScriptConfig config, params string[] startupAssemblies)
    {
#if (!UNITY_WEBGL || UNITY_EDITOR) && !DISABLE_FM_MONO
        FM_MonoEnv.Init(config.libraryDirectory, config.configDirectory, config.overrideConfig, config.imageLoadFunc);
        foreach (var ab in startupAssemblies)
        {
            FM_MonoEnv.LoadAssembly(ab);
        }

        this._scriptTickUpdator = GlobalScheduler.AddFrameLimitUpdator(1, this.OnUpdate);
#endif
    }

#if (UNITY_WEBGL && !UNITY_EDITOR) || DISABLE_FM_MONO
    private static string GetClassFullName(string assemblyName, string nameSpace, string className)
    {
        // webgl用单独脚本域dll名
        assemblyName = ASSEMBLY_NAME;
        if (!string.IsNullOrEmpty(assemblyName))
        {
            assemblyName = ", " + assemblyName;
        }

        if (!string.IsNullOrEmpty(nameSpace))
        {
            nameSpace = nameSpace + ".";
        }

        return nameSpace + className + assemblyName + CLASS_SUFFIX;
    }
#endif

    public T InvokeStaticMethodRet<T>(string assemblyName, string nameSpace, string className, string methodName,
        params object[] args)
    {
#if (!UNITY_WEBGL || UNITY_EDITOR) && !DISABLE_FM_MONO
        IntPtr resultPtr = FM_MonoEnv.InvokeMonoStaticMethod(assemblyName, nameSpace, className, methodName, args);
        if (resultPtr == IntPtr.Zero)
        {
            return default(T);
        }

        return FM_MonoHelper.GetGenericReturn<T>(resultPtr);
#else
        Type type = Type.GetType(GetClassFullName(assemblyName, nameSpace, className));
        var result =
            type?.InvokeMember(methodName,
                System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static |
                System.Reflection.BindingFlags.InvokeMethod, null, null,
                args);
        if (result == null)
        {
            return default(T);
        }

        return (T)result;
#endif
    }

    public void InvokeStaticMethod(string assemblyName, string nameSpace, string className, string methodName,
        params object[] args)
    {
#if (!UNITY_WEBGL || UNITY_EDITOR) && !DISABLE_FM_MONO
        FM_MonoEnv.InvokeMonoStaticMethod(assemblyName, nameSpace, className, methodName, args);
#else
        var classFullName = GetClassFullName(assemblyName, nameSpace, className);
        Type type = Type.GetType(classFullName);
        type?.InvokeMember(methodName,
            System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static |
            System.Reflection.BindingFlags.InvokeMethod, null, null,
            args);
#endif
    }

    private void OnUpdate(object param)
    {
#if (!UNITY_WEBGL || UNITY_EDITOR) && !DISABLE_FM_MONO
        FM_MonoEnv.Tick();
#endif
    }

    public void Cleanup()
    {
#if (!UNITY_WEBGL || UNITY_EDITOR) && !DISABLE_FM_MONO
        GlobalScheduler.RemoveLimitUpdator(ref this._scriptTickUpdator);

        FM_MonoEnv.Cleanup();
#endif
    }
}